feat(bin): add standalone morph-statetest runner#113
Conversation
morph-statetest's schema was routing the JSON `type` field through
revm's `TransactionType::from(u8)`, which only knows the 5 vanilla
Ethereum tx types (Legacy=0, Eip2930=1, Eip1559=2, Eip4844=3,
Eip7702=4). Any other value -- including Morph's `0x7F` MorphTx and
`0x7E` L1Msg -- falls into `Custom`, which has discriminant `0xFF`.
The subsequent `as u8` cast then yields `0xFF`, so `TxEnv.tx_type =
0xFF` instead of the intended raw byte.
Downstream, `MorphTxExt::is_morph_tx` checks `tx_type ==
MORPH_TX_TYPE_ID (0x7F)` and `is_l1_msg` checks against
`L1_TX_TYPE_ID (0x7E)`. Both returned false for every Morph-typed
statetest fixture, so:
- The MorphTx fee handler dispatch in `crates/revm/src/handler.rs`
(`if evm.ctx_ref().tx().is_morph_tx() { ... }`) silently fell
through to the standard ETH-fee branch, even when `feeTokenID > 0`.
- The L1Msg short-circuit in the same handler never activated.
Empirically caught 2026-05-18 via the morph-cross-client-tests harness
during investigation of fuzz coverage in fee_token_call. After the
matching fix landed on the geth side (gen_sttransaction.go now parses
feeTokenID instead of dropping it), geth produced a stateRoot
distinct from the no-fee-fields baseline while reth's stateRoot
stayed at the baseline -- confirming reth's alt-token fee path was
never activating in statetest. With the raw-byte fix here, reth's
fee handler engages and now produces a stateRoot that differs from
the baseline too.
Fix: short-circuit the enum round-trip when JSON explicitly supplies a
`type` field. Only fall through to revm's `TransactionType` inference
for fixtures without a `type` (where the enum's standard-types-only
view is sufficient anyway).
There was a problem hiding this comment.
Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.
Once credits are available, reopen this pull request to trigger a review.
|
Warning Review limit reached
More reviews will be available in 46 minutes and 37 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…back with go-ethereum - Set block.beneficiary to the Morph fee vault on Curie+ so statetest fee distribution matches morph-geth's FeeVaultEnabled path. - Build the per-tx receipt log set from pre-fee + EVM + post-fee logs so logsRoot covers the full receipt surface (not just EVM execution logs). - Plumb post.txbytes + has_base_fee into morph_tx_env; when txbytes is absent, fall back to encode_for_l1_fee so L1 data fee accounting matches the geth EstimateL1DataFeeForMessage path. - Default gas_priority_fee to gas_price when has_base_fee is true, matching the geth-side gasTipCap default for legacy/EIP-2930 txs. - Switch encode_for_l1_fee's placeholder signature to (U256::MAX, U256::MAX, true) so the encoded byte length matches go-ethereum's 0xff...ff placeholder and L1 fee sizing is identical.
…tly zeroing L1 fee morph-reth's transaction envelope has no Eip4844 variant (crates/primitives/src/transaction/envelope.rs), so encode_for_l1_fee cannot fabricate one for a blob tx. The schema previously skipped fallback RLP generation for blob_hashes != empty and let it fall through to handler.rs's rlp_bytes.unwrap_or_default(), which produces empty bytes and therefore L1 fee = 0. A future blob-tx fuzz reactivation would silently diverge from go-ethereum's L1 fee accounting with no hint of why. Surface the impossibility at parse time with a new SchemaError variant. Add a test that asserts blob_hashes != empty without post.txbytes errors out with BlobTxRequiresTxBytes.
The cherry-picked source commits predate the rustfmt config currently enforced by CI. Re-runs only `cargo fmt --all`, no semantic changes.
011bf07 to
fbff3f4
Compare
Summary
morph-statetest <path>binary that runs a GeneralStateTest with Morph EVM semanticsmorph-cross-client-tests/docs/statetest-contract.mdmorph-l2/morph-cross-client-testspost.<fork>fixture keysTest plan
cargo test -p morph-statetestcargo clippy -p morph-statetest --all-targets -- -D warningsmorph-statetest testdata/fee_token_call_jade.jsonproduces valid JSON Lines outputevm --json statetest: 0/3900 mismatches on full 39-engine sweep across Jade fork